﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JelleDruyts.Mollom.Client.Test
{
    [TestClass]
    public class MollomClientTest
    {
        #region Constants

        /// <summary>
        /// A dummy session id.
        /// </summary>
        private const string DummySessionId = "dummy-session-id";

        #endregion

        #region Constructor - Negative Tests

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ConstructorShouldFailOnNullPublicKey()
        {
            MollomClient mollom = new MollomClient("dummy", null);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ConstructorShouldFailOnEmptyPublicKey()
        {
            MollomClient mollom = new MollomClient("dummy", string.Empty);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ConstructorShouldFailOnNullPrivateKey()
        {
            MollomClient mollom = new MollomClient(null, "dummy");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ConstructorShouldFailOnEmptyPrivateKey()
        {
            MollomClient mollom = new MollomClient(string.Empty, "dummy");
        }

        #endregion

        #region CheckContent - Positive Tests

        [TestMethod]
        public void CheckContentShouldReportSpam()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            ContentCheck result = client.CheckContent(null, "spam");
            AssertContentCheck(result, ContentClassification.Spam, 0);

            // Call other overloads too.
            AssertContentCheck(client.CheckContent(null, "spam", null), ContentClassification.Spam, 0);
            AssertContentCheck(client.CheckContent(null, "spam", null, null, null, null), ContentClassification.Spam, 0);
            AssertContentCheck(client.CheckContent(null, "spam", null, null, null, null, null), ContentClassification.Spam, 0);
            AssertContentCheck(client.CheckContent(null, "spam", null, null, null, null, null, null, null), ContentClassification.Spam, 0);
        }

        [TestMethod]
        public void CheckContentShouldReportHam()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            ContentCheck result = client.CheckContent(null, "ham");
            AssertContentCheck(result, ContentClassification.Ham, 1);
        }

        [TestMethod]
        public void CheckContentShouldReportUnsure()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            ContentCheck result = client.CheckContent(null, "unsure");
            AssertContentCheck(result, ContentClassification.Unsure, 0.5);
        }

        #endregion

        #region SendFeedback - Positive Tests

        [TestMethod]
        public void SendFeedbackShouldSucceedForAllTypes()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.SendFeedback(DummySessionId, Feedback.LowQuality);
            client.SendFeedback(DummySessionId, Feedback.Profanity);
            client.SendFeedback(DummySessionId, Feedback.Spam);
            client.SendFeedback(DummySessionId, Feedback.Unwanted);
        }

        #endregion

        #region SendFeedback - Negative Tests

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void SendFeedbackShouldThrowOnNullSession()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.SendFeedback(null, Feedback.Spam);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void SendFeedbackShouldThrowOnEmptySession()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.SendFeedback(string.Empty, Feedback.Spam);
        }

        [TestMethod]
        public void SendFeedbackShouldThrowOnInvalidFeedback()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();

            try
            {
                client.SendFeedback(DummySessionId, (Feedback)(-1));

                // The call above should fail, so we shouldn't be getting here.
                Assert.Fail();
            }
            catch (Exception exc)
            {
                Assert.IsInstanceOfType(exc, typeof(XmlRpcFaultException));
                XmlRpcFaultException faultExc = (XmlRpcFaultException)exc;
                Assert.AreEqual<int>(1000, faultExc.FaultCode);
            }
        }

        #endregion

        #region GetImageCaptcha - Positive Tests

        [TestMethod]
        public void GetImageCaptchaShouldReturnValidCaptcha()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            Captcha captcha = client.GetImageCaptcha();
            Assert.IsNotNull(captcha);
            Assert.IsFalse(string.IsNullOrEmpty(captcha.Url));
            Assert.IsFalse(string.IsNullOrEmpty(captcha.SessionId));

            // Call other overloads too.
            captcha = client.GetImageCaptcha(null);
            Assert.IsNotNull(captcha);
            Assert.IsFalse(string.IsNullOrEmpty(captcha.Url));
            Assert.IsFalse(string.IsNullOrEmpty(captcha.SessionId));
        }

        #endregion

        #region GetAudioCaptcha - Positive Tests

        [TestMethod]
        public void GetAudioCaptchaShouldReturnValidCaptcha()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            Captcha captcha = client.GetAudioCaptcha();
            Assert.IsNotNull(captcha);
            Assert.IsFalse(string.IsNullOrEmpty(captcha.Url));
            Assert.IsFalse(string.IsNullOrEmpty(captcha.SessionId));

            // Call other overloads too.
            captcha = client.GetAudioCaptcha(null);
            Assert.IsNotNull(captcha);
            Assert.IsFalse(string.IsNullOrEmpty(captcha.Url));
            Assert.IsFalse(string.IsNullOrEmpty(captcha.SessionId));
        }

        #endregion

        #region CheckCaptcha - Positive Tests

        [TestMethod]
        public void CheckCaptchaToBeCorrect()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            bool correct = client.CheckCaptcha(DummySessionId, "correct");
            Assert.IsTrue(correct);
        }

        [TestMethod]
        public void CheckCaptchaToBeIncorrect()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            bool correct = client.CheckCaptcha(DummySessionId, "incorrect");
            Assert.IsFalse(correct);
        }

        #endregion

        #region CheckCaptcha - Negative Tests

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void CheckCaptchaShouldThrowOnNullSession()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.CheckCaptcha(null, "dummy");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void CheckCaptchaShouldThrowOnEmptySession()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.CheckCaptcha(string.Empty, "dummy");
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void CheckCaptchaShouldThrowOnNullSolution()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.CheckCaptcha("dummy", null);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void CheckCaptchaShouldThrowOnEmptySolution()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.CheckCaptcha("dummy", string.Empty);
        }

        #endregion

        #region GetStatistics - Positive Tests

        [TestMethod]
        public void GetStatisticsShouldReturnConsistentValuesForKnownTypes()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();

            int valueFromString, valueFromEnum;

            valueFromString = client.GetStatistics("total_days");
            valueFromEnum = client.GetStatistics(StatisticsTypes.TotalDays);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);

            valueFromString = client.GetStatistics("total_accepted");
            valueFromEnum = client.GetStatistics(StatisticsTypes.TotalAccepted);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);

            valueFromString = client.GetStatistics("total_rejected");
            valueFromEnum = client.GetStatistics(StatisticsTypes.TotalRejected);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);

            valueFromString = client.GetStatistics("yesterday_accepted");
            valueFromEnum = client.GetStatistics(StatisticsTypes.YesterdayAccepted);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);

            valueFromString = client.GetStatistics("yesterday_rejected");
            valueFromEnum = client.GetStatistics(StatisticsTypes.YesterdayRejected);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);

            valueFromString = client.GetStatistics("today_accepted");
            valueFromEnum = client.GetStatistics(StatisticsTypes.TodayAccepted);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);

            valueFromString = client.GetStatistics("today_rejected");
            valueFromEnum = client.GetStatistics(StatisticsTypes.TodayRejected);
            Assert.IsTrue(valueFromString >= 0);
            Assert.IsTrue(valueFromEnum >= 0);
            Assert.AreEqual<int>(valueFromString, valueFromEnum);
        }

        #endregion

        #region GetStatistics - Negative Tests

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GetStatisticsShouldFailOnNullType()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.GetStatistics(null);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GetStatisticsShouldFailOnEmptyType()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            client.GetStatistics(string.Empty);
        }

        [TestMethod]
        public void GetStatisticsShouldFailOnMultipleTypes()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();

            try
            {
                client.GetStatistics(StatisticsTypes.TotalDays | StatisticsTypes.TotalRejected);

                // The call above should fail, so we shouldn't be getting here.
                Assert.Fail();
            }
            catch (Exception exc)
            {
                Assert.IsInstanceOfType(exc, typeof(XmlRpcFaultException));
                XmlRpcFaultException faultExc = (XmlRpcFaultException)exc;
                Assert.AreEqual<int>(1000, faultExc.FaultCode);
            }
        }

        #endregion

        #region VerifyKey - Positive Tests

        [TestMethod]
        public void VerifyKeyShouldBeCorrectForDeveloperKey()
        {
            MollomClient client = GetValidMollomClientForUnitTesting();
            bool result = client.VerifyKey();
            Assert.IsTrue(result);
        }

        #endregion

        #region VerifyKey - Negative Tests

        [TestMethod]
        public void VerifyKeyShouldThrowOnDisabledKey()
        {
            MollomClient client = new MollomClient("disabled-key", "disabled-key");

            try
            {
                bool result = client.VerifyKey();

                // The call above should fail, so we shouldn't be getting here.
                Assert.Fail();
            }
            catch (Exception exc)
            {
                Assert.IsInstanceOfType(exc, typeof(XmlRpcFaultException));
                XmlRpcFaultException faultExc = (XmlRpcFaultException)exc;
                Assert.AreEqual<int>(1000, faultExc.FaultCode);
            }
        }

        [TestMethod]
        public void VerifyKeyShouldThrowOnUnknownKey()
        {
            MollomClient client = new MollomClient("unknown-key", "unknown-key");

            try
            {
                bool result = client.VerifyKey();

                // The call above should fail, so we shouldn't be getting here.
                Assert.Fail();
            }
            catch (Exception exc)
            {
                Assert.IsInstanceOfType(exc, typeof(XmlRpcFaultException));
                XmlRpcFaultException faultExc = (XmlRpcFaultException)exc;
                Assert.AreEqual<int>(1000, faultExc.FaultCode);
            }
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Asserts that a <see cref="ContentCheck"/> is as expected.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="expectedClassification">The expected classification.</param>
        /// <param name="expectedQuality">The expected quality.</param>
        private static void AssertContentCheck(ContentCheck result, ContentClassification expectedClassification, double expectedQuality)
        {
            Assert.IsNotNull(result);
            Assert.AreEqual<ContentClassification>(expectedClassification, result.Classification);
            Assert.AreEqual<double>(expectedQuality, result.Quality);
            Assert.IsFalse(string.IsNullOrEmpty(result.SessionId));
        }

        private static MollomClient GetValidMollomClientForUnitTesting()
        {
            // This private/public key pair is registerd to http://www.codeplex.com/mollomnet,
            // which is configured as "Developer Mode" so that it can be used for unit testing.
            return new MollomClient("3ffbc87fd5c265e94b75b27bfca58642", "581fe0dfcc97aec49942c1d000eb25f2", "UnitTestAgent");
        }

        #endregion
    }
}